package com.alibaba.cola.event.core;

import com.alibaba.cola.event.EventHandler;
import com.alibaba.cola.event.EventHandlerI;
import com.alibaba.cola.event.common.EventI;
import com.alibaba.cola.event.core.EventHub;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;

/**
 * 事件注册配置类
 *
 * @author xionglei
 * @version 1.0.0
 * 2021年11月22日 14:10
 */
@Component
public class EventRegister {

    @Autowired
    private EventHub eventHub;

    /**
     * 注册事务处理器
     *
     * @param eventHandlerI
     */
    public void doRegistration(EventHandlerI eventHandlerI) {
        Class<?> extensionClz = eventHandlerI.getClass();
        if (AopUtils.isAopProxy(eventHandlerI)) {
            extensionClz = ClassUtils.getUserClass(eventHandlerI);
        }
        EventHandler eventHandlerAnn = AnnotationUtils.findAnnotation(extensionClz, EventHandler.class);
        if (eventHandlerAnn == null) {
            throw new RuntimeException("EventHandler class not found @EventHandler");
        }
        Class<? extends EventI> eventClz = getEventFromExecutor(extensionClz);
        eventHub.register(eventClz, eventHandlerI);
    }

    private Class<? extends EventI> getEventFromExecutor(Class<?> eventExecutorClz) {
        Method[] methods = eventExecutorClz.getDeclaredMethods();
        for (Method method : methods) {
            if (isExecuteMethod(method)) {
                return checkAndGetEventParamType(method);
            }
        }
        throw new RuntimeException("Event param in " + eventExecutorClz + " execute" + "() is not detected");
    }

    private boolean isExecuteMethod(Method method) {
        return "execute".equals(method.getName()) && !method.isBridge();
    }

    private Class checkAndGetEventParamType(Method method) {
        Class<?>[] exeParams = method.getParameterTypes();
        if (exeParams.length == 0) {
            throw new RuntimeException("Execute method in " + method.getDeclaringClass() + " should at least have one parameter");
        }
        if (!EventI.class.isAssignableFrom(exeParams[0])) {
            throw new RuntimeException("Execute method in " + method.getDeclaringClass() + " should be the subClass of EventI");
        }
        return exeParams[0];
    }

}
